#include <gtest/gtest.h>

//TODO: 1. remove the auto open-close for SimpleDB;
//TODO: 2. add the AutoManager for SimpleDB with Decorator
//TODO: memdb::ITable interface : 
//  1. add exists()  
//  2. add flag for replace 
//  3. add impl of SimpleTable: return UpdateFailedError(-1)  if update affect_rows =0 

#include "memdb/impl/simple/SimpleDB.h"

using namespace memdb::impl::simple;

static const char * db_dir = "simpledb";
static const char * tabname = "tab1";
class SimpleDBTest: public testing::Test {
protected:
  void SetUp() override {
    db = new SimpleDB(db_dir); 

    auto && tabs = db->tables();
    auto && pair = tabs.create(tabname);
    ASSERT_TRUE(pair.second);
    tab = pair.second;
    ASSERT_TRUE(tabs.exists(tabname));
  }

  void TearDown() override {
    db->destroy();
    delete db;
  }
  
protected:
  SimpleDB * db;
  memdb::ITable * tab;
};


TEST_F(SimpleDBTest, testDB){
  const std::string key = "hello";
  const std::string  expect_value = "world";

  ASSERT_EQ(0ull, tab->count());
  //insert
  ASSERT_EQ(std::error_code(), tab->insert(key, expect_value));

  ASSERT_EQ(1ull, tab->count());

  //get
  std::string value;
  ASSERT_EQ(std::error_code(), tab->get(key, value));
  ASSERT_EQ(expect_value, value);

  //udpate
  const std::string expect_new_value = "world......";
  ASSERT_EQ(std::error_code(), tab->update(key, expect_new_value));

  //get
  value.clear(); 
  ASSERT_EQ(std::error_code(), tab->get(key, value));
  ASSERT_EQ(expect_new_value, value);

  //replace  with exist record
  ASSERT_EQ(std::error_code(), tab->replace(key, expect_value));
  value.clear(); 
  ASSERT_EQ(std::error_code(), tab->get(key, value));
  ASSERT_EQ(expect_value, value);

  //replace with no-exist kv
  const std::string new_key = "hello1111";
  ASSERT_EQ(std::error_code(), tab->replace(new_key, expect_value));
  value.clear(); 
  ASSERT_EQ(std::error_code(), tab->get(new_key, value));
  ASSERT_EQ(expect_value, value);

 ASSERT_EQ(2ull, tab->count());


  //update with old value : precondtion: new_key:expect_value
  value.clear();
  ASSERT_EQ(std::error_code(), tab->update(new_key, expect_new_value, value));
  ASSERT_EQ(expect_value, value);//value is output for old
  //post-condtion: value == expect_new_value 
  value.clear(); 
  ASSERT_EQ(std::error_code(), tab->get(new_key, value));
  ASSERT_EQ(expect_new_value, value);

  //replace with old value, precondtion: (new_key, expect_new_value)
  value.clear();
  ASSERT_EQ(std::error_code(), tab->update(new_key, expect_value, value));
  ASSERT_EQ(expect_new_value, value);//value is output for old
  //post-condtion: value == expect_value 
  value.clear(); 
  ASSERT_EQ(std::error_code(), tab->get(new_key, value));
  ASSERT_EQ(expect_value, value);

  //remove
  ASSERT_EQ(std::error_code(), tab->remove(key));
  ASSERT_NE(std::error_code(), tab->get(key, value));
}

TEST_F(SimpleDBTest, testDropTable) {
  auto && tabs = db->tables();
  tabs.drop(tabname);
  ASSERT_FALSE(tabs.exists(tabname));
}

TEST_F(SimpleDBTest, testDbDestroy) {
  ASSERT_TRUE(std::filesystem::exists(db->dbPath()));

  db->destroy();

  ASSERT_TRUE(false == std::filesystem::exists(db->dbPath()));
}



class SimpleDBDataTest: public SimpleDBTest {
protected:
  void SetUp() override {
    SimpleDBTest::SetUp();

    ASSERT_EQ(std::error_code(), tab->insert("1", "1222"));
    ASSERT_EQ(std::error_code(), tab->insert("2", "2222"));
    ASSERT_EQ(std::error_code(), tab->insert("3", "3222"));
    ASSERT_EQ(std::error_code(), tab->insert("4", "4222"));
    ASSERT_EQ(std::error_code(), tab->insert("5", "5222"));
    ASSERT_EQ(5ull, tab->count());
  }

  void TearDown() override {
    SimpleDBTest::TearDown();
  }

  std::string value;
};


TEST_F(SimpleDBDataTest, testRangeScan){
  tab->iterator()->foreach([](auto & item){
    std::cout << "all: "  << item->first << "|" << item->second << std::endl;
  });
  tab->range((size_t)0, (size_t)2)->foreach([](auto & item){
    std::cout << "index 0 len 2:"<< item->first << "|" << item->second << std::endl;
  });
  tab->findRange("3", (size_t)0, (size_t)2)->foreach([](auto & item){
    std::cout << "key-3 len 2:"<< item->first << "|" << item->second << std::endl;
  });
}

TEST_F(SimpleDBDataTest, testTableExists){
  size_t size = tab->count();
  ASSERT_TRUE(tab->exists(std::to_string(size - 1)));
  ASSERT_FALSE(tab->exists(std::to_string(size + 1)));
}

TEST_F(SimpleDBDataTest, testReplaceExists){
  std::string key = "3";
  std::string expect_value = "new_value";
  std::string old_value = "";
  std::string expect_old_value = "3222";

  auto && [ec, is_repalce] = tab->replace(key, expect_value, old_value);
  ASSERT_EQ(std::error_code(), ec);
  ASSERT_TRUE(is_repalce);
  ASSERT_EQ(expect_old_value, old_value);

  ASSERT_EQ(std::error_code(), tab->get(key, value));
  ASSERT_EQ(expect_value, value);
}

TEST_F(SimpleDBDataTest, testReplaceNotExists){
  std::string key = "6";
  std::string expect_value = "new_value";
  std::string old_value = "";

  auto && [ec, is_repalce] = tab->replace(key, expect_value, old_value);
  ASSERT_EQ(std::error_code(), ec);
  ASSERT_FALSE(is_repalce);

  ASSERT_EQ(std::error_code(), tab->get(key, value));
  ASSERT_EQ(expect_value, value);
}
